package com.gzsxy.esjy.service.netty.common.lock.impl;

import com.gzsxy.esjy.common.Assert;
import com.gzsxy.esjy.service.netty.common.lock.DistributedLock;
import com.gzsxy.esjy.service.netty.common.lock.Lock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Function;


/**
 * 同步锁获取(内部实现)
 * @author zhangxiaolong
 * @date 2022/7/11 9:36
 */
@Component
//@ConditionalOnMissingBean(value=DistributedLock.class)
public class DefaultLockHandler implements DistributedLock {

	private static final Logger log = LoggerFactory.getLogger(DefaultLockHandler.class);

	private final Map<String, Boolean> map = new ConcurrentHashMap<String, Boolean>();
		
	private final Map<String,Map<String, String>> asyncMap = new HashMap<String, Map<String, String>>();
	
	@Value("${gosun.synchronized.hash.device:24}")
	private int devices ;
	
	@PostConstruct
	private void init() {
		initDevice(DistributedLock.DEFAULT_DEVICE) ;
	}
	
	private Map<String, String> initDevice(String device) {
		synchronized (asyncMap) {
			Map<String, String> defaultAsyncMap = new HashMap<String, String>() ;
			for(int i = 0 ;i < devices; i++) {
				defaultAsyncMap.put(""+i, "") ;
			}
			asyncMap.put(device, defaultAsyncMap) ;
			return defaultAsyncMap;
		}
		
	}
	
	@Override
	public boolean tryLock(String key,String device,long maxWaitTime, long timeInterval) {
		int count = (int) (maxWaitTime / timeInterval);
		int i = 0;
		while (i < count) {
			if (getLock(key,device)) {
				return true;
			}
			try {
				Thread.sleep(timeInterval);
			} catch (InterruptedException e) {
				log.error(e.getMessage(), e);
			}
			i++;
		}
		return false;
	}
	
	/**
	 * 获取锁
	 * 
	 * @param key
	 * @param
	 * @return
	 */
	private boolean getLock(String key,String device) {
		synchronized (getKey(Math.abs(key.hashCode() % devices)+"",device)) {
			if (!map.containsKey(key)) {
				map.put(key, true);
				log.info("get key: {}", key);
				return true;
			} else {
				return false;
			}
		}
	}
	
	/**
	 * 获取键
	 * @param hascode
	 * @param device
	 * @return
	 */
	private String getKey(String hascode,String device) {
		Map<String, String> map2 = asyncMap.get(device);
		if(map2 == null) {
			map2 = initDevice(device);
		}
		return map2.get(hascode) ;
	}

	@Override
	public void unLock(String key,String device) {
		if (null != key) {
			map.remove(key);
			log.info("remove key: {}", key);
		}
	}

	@Override
	public Object excute(Function<Lock,Object> excute, Lock lock) {
		Object result = null ;
		boolean flag = false ;
		try {
			if(tryLock(lock.getKey(), lock.getGroup(),lock.getMaxWaitTime(),lock.getTimeInterval())) {
				flag = true ;
				result = excute.apply(lock);
			}else {
				//获取锁超时
				Assert.isNotNull(null,lock.getFailMsg(),504);
			}
		}finally {
			if(flag) {
				unLock(lock.getKey(), lock.getGroup());
			}
		}
		return result;
	}

	@Override
	public void excute(Lock lock, Consumer<Lock> excute) {
		excute(e->{
			excute.accept(lock);
			return null ;
		},lock);
	}
}
